package ranab.jar.view;

import java.util.*;
import java.util.zip.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import ranab.jar.*;
import ranab.gui.GuiUtils;

/**
 * This tree model stores jar/zip data.
 *
 * @author <a href="mailto:rana_b@yahoo.com">Rana Bhattacharyya</a>
 */
public
class JarTree implements TreeModel, JarObserver, JarUI {
	
	private TreeNode mRoot;
    private TreeModelEvent mRootEvent;
	private Vector mListenrList;
	private Component mParent;
    private JTree mjTree;
	private JScrollPane mjScrollPane;
	private JPanel mjTopPane;
	
	private boolean mbIsActive; 
	
	public JarTree(Component comp)  {
		mListenrList = new Vector();
		mParent = comp;
		mbIsActive = false;
		initComponents();
	}
	
	private void initComponents()  {
		mRoot = new TreeNode(null, ".", null);
        mRootEvent = new TreeModelEvent(this, new TreePath(mRoot));
		mjTree = new JTree(this);
		mjTree.setEditable(false);
		
		JScrollPane scrollPane = new JScrollPane(mjTree, 
									JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                               		JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		mjTopPane = new JPanel();
		mjTopPane.setLayout(new BorderLayout());
		mjTopPane.add(scrollPane);						
	}
	
	/**
	 * Initialize tree
	 */
	public void start()  {
		mRoot.clear();
        structureChanged();
	}
	
	/**
	 * Add new entry.
	 */
	public void setNext(ZipEntry entry)  {
		String name = entry.getName();
		StringTokenizer st = new StringTokenizer(name, "/");
		
		TreeNode parent = mRoot;
		String currAbsPath = mRoot.getAbsolutePath();
		while (st.hasMoreTokens())  {
			String tok = st.nextToken();
			currAbsPath += '/' + tok;
			TreeNode thisNode = parent.getNode(currAbsPath);
			if (thisNode == null)  {
				thisNode = new TreeNode(parent, tok, entry);
                structureChanged();
			}
			parent = thisNode;
		}
	}
	
	/**
	 * Set count.
	 */
	public void setCount(int count)  {
	}
	
	/**
	 * Jar file processing end.
	 */
	public void end()  {
        structureChanged();
        mjTree.expandPath(mRootEvent.getTreePath());
	}
	
	/**
     * Update component UI.
     */
    public void updateLnF() {
		SwingUtilities.updateComponentTreeUI(mjTopPane);
    }
	
	/**
     * Get the scroll pane that contains the tree.
     */
    public JPanel getPanel() {
        return mjTopPane;
    }
 
    /**
	 * Is active.
	 */
	public boolean isActive()  {
		return mbIsActive;
	}      
	
	/**
	 * Set enable tag.
	 */
	public void setActive(boolean b)  {
		mbIsActive = b;
	}
	
	/**
	 * Get selected entries.
	 */
	public ZipEntry[] getSelectedEntries()  {
		TreePath selectedItems[] = mjTree.getSelectionPaths();
		if (selectedItems == null || selectedItems.length == 0)  {
			return null;
		}
		Vector vec = new Vector();
		for(int i=0; i<selectedItems.length; i++)  {
            TreeNode node = (TreeNode)selectedItems[i].getLastPathComponent();
			if(node.isLeaf()) {
                ZipEntry entry = node.getEntry();
                if(!vec.contains(entry)) {
                    vec.add(entry);
                }
            }
            else {
                getLeaves(node, vec);
            }
		}
        
		if (vec.size() == 0)  {
			return null;
		}
		
		ZipEntry[] entries = new ZipEntry[vec.size()];
		for(int i=0; i<entries.length; i++)  {
			entries[i] = (ZipEntry)vec.get(i);
		}
		return entries;
	} 
	
    /**
     * Get children entries.
     */
    private void getLeaves(TreeNode node, Vector vec) {
        
        // check size
        if( (node == null) || (node.size() == 0) ) {
            return;
        }
        
        // check zipentry
        for(int i=0; i<node.size(); i++) {
            TreeNode child = (TreeNode)node.elementAt(i);
            if(child.isLeaf()) {
                ZipEntry entry = child.getEntry();
                if(!vec.contains(entry)) {
                    vec.add(entry);
                }
            }
            else {
                getLeaves(child, vec);
            }
        }
    }
     
    
	/**
	 * Send structure change event.
	 */
	private void structureChanged()  {
		int sz = mListenrList.size();

        for (int i=sz-1; i>=0; i--) {
            ((TreeModelListener)mListenrList.elementAt(i)).treeStructureChanged(mRootEvent);
        } 
	}
	
	/**
	 * Set error message.
	 */
	public void setError(String errMsg)  {
		if (isActive())  {
			GuiUtils.showErrorMessage(mParent, errMsg);
		}
	}
	
	
	/**
     * Add a listener.
     */
    public void addTreeModelListener(TreeModelListener l) {
        mListenrList.add(l);
    }
	
	/**
     * Remove a listener.
     */
    public void removeTreeModelListener(TreeModelListener l) {
        mListenrList.remove(l);
    }
	
   /**
    * Object changed. In our case it is not possible - so
    * igmore it.
    */
    public void valueForPathChanged(TreePath path, Object newValue) {
    }
	
	/**
	 * Get child at an index.
	 */
	 public Object getChild(Object parent, int index)  {
	 	return ((TreeNode)parent).get(index);
	 }
	 
	/**
	 * Get child count.
	 */
	 public int getChildCount(Object parent)  {
	  	return ((TreeNode)parent).size();
	 } 
	  
	 /**
	  * Get root node.
	  */
	  public Object getRoot()  {
	   	return mRoot;
	  }
	  
	 /**
	  * Is a leaf
	  */
	  public boolean isLeaf(Object node)  {
	  	return ((TreeNode)node).isLeaf();
	  }
	  
	 /**
	  * Get index of child.
	  */
	 public int getIndexOfChild(Object parent, Object child)  {
		return ((TreeNode)parent).indexOf(child);
	 }  
	 
}